LScript User Guide
Next Section Previous Section Table of Contents Index Errata

LightWave 3D Layout Commands and Functions

LScript for Layout

The Layout LScript system consists of five LightWave 3D Layout plug-ins designed to bring plug-in prototyping capabilities to LightWave 3D Layout. These new plug-ins cover the following five Layout plug-in architectures:

             Image Filter         (ls-if.p)
             Object Replacement   (ls-or.p)
             Displacement Map     (ls-dm.p)
             Procedural Texture   (ls-pt.p)
             Item Animation       (ls-ia.p)
             Layout Generic       (ls-gn.p)

Assumptions

Because Layout LScript builds upon the functionality available in the LScript core engine, if you are unfamiliar with the the LScript language or the techniques used to develop LScripts, we recommend that you refer to and review the previous sections of this document before continuing.

Layout Script Structure

In Modeler LScript, scripts are written to be executed as though they were complete programs, having clearly defined beginnings, middles, and ends. When a Modeler LScript reaches the end of its execution, you know absolutely that LightWave Modeler will not invoke that script again until you specifically request it by starting the LScript plug-in again. While a Modeler LScript is executing, no other processing may take place within LightWave Modeler until the script terminates. This type of operation is known as "modal" or "synchronous." Many graphical dialog boxes, including most used in Microsoft Windows to perform user input, operate in this fashion.

With the notable exception of Layout Generic scripts, Layout scripting, on the other hand, presents a new method of script execution that causes an LScript to be activated only once, while it remains active until it is specifically stopped by either the termination of LightWave Layout, or the explicit deactivation of the Layout LScript plug-in from the plug-in panel. The script remains active even while you perform other functions within LightWave Layout. LightWave Layout scripts are designed to partake in this type of cooperative execution.

To facilitate this cooperative execution, Layout LScript scripting is centered completely around the concept of "call-back" functions. A call-back function is one designed to be called at any time, usually to respond to some specific event or situation. Until these call-back functions are activated, they remain "asleep," taking up no CPU time and having no influence on the processing of LightWave Layout.

Figure #1 illustrates the flow of execution in Modeler LScript.



Figure #1
Modeler LScript Execution Flow
Modeler LScript has a very linear flow of execution. Execution starts in the main() function, flows through any intervening logic or function calls, and then reaches the end of its execution when the main() function terminates (by reaching either a 'return' statement or the logical end of the function).

Figure #2, however, illustrates the dramatic difference in execution flow for a Layout LScript.



Figure #2
Layout LScript Execution Flow
With the exception of Layout Generic LScript, a Layout LScript contains not one well-defined point of execution for a script, but rather many. In fact, a Layout LScript can really be thought of as being many individual scripts all contained in the same file and relating to the same functionality. Layout can invoke these "individual" scripts (functions) at any time without executing, or requiring the assistance of, any other.

As mentioned previously, Layout Generic scripts are the exeception to this mechanism. The reason for this exception is that Layout Generic plug-ins are not associated with any Layout component (Camera, Mesh object, Light, etc.), and as such, are not involved in the generation of frames of animation. In fact, Layout Generic scripts behave almost identically to Modeler scripts in that they have a single point of entry, and their execution begins and ends with this function. Layout Generic scripts, however, have full access to all non-architecture-specific Layout components that all other Layout scripts enjoy. The section documenting Layout Generic LScripts later on this page provides more detail.

Having a clear understanding of these mechanisms is essential to the discussions that follow. Please take this time to examine the available example scripts for additional clarification, if necessary.

Shared Functions

While each plug-in architecture can require its own specific call-back function, each Layout script can provide one or more "common" entry points that LightWave Layout can invoke, regardless of the architecture being scripted. These common functions are all optional, and Layout LScript will supply internal--but "empty"--versions for LightWave Layout to execute if you omit them from your script. These common function are:

       process()

                   this call-back function is used to perform the main
                   processing of the script, if any (similar to main() in
                   Modeler scripting).  it can be (and usually is) invoked
                   more than once.

       create()

                   called once when the script is loaded, immediately upon
                   successful processsing of the script by the LScript core
                   engine.  it is used to perform "startup" processing for a
                   script.

       destroy()

                   called once when the script is deactivated from the plug-in
                   panel or removed from memory.  used to perform "cleanup"
                   processing for a script.

       save()

                   called when a scene file is saved to disk by the user.  at
                   this time, the script can store any script-related values
                   to be restored when the scene is reloaded into LightWave
                   Layout. in either a scene (ASCII) or object (binary) file.
                   this function will be called each time the scene is saved
                   to disk.

       load()

                   used to restore any previously-saved script-related values
                   from either a scene or object file

       options()

                   invoked when the the "Options" button of the script is
                   selected.  standard requester functions, identical to those
                   available in Modeler LScript, can be used to generate a
                   user-interface and to allow the user to modify operational
                   parameters.  the LightWave Panel Services Global-Class
                   plug-in must be available for successful requester dialog
                   creation to occur.  The LW Panel Services plug-in enables
                   Layout LScript to offer several "extended" control types,
                   discussed in Appendix C.
A Layout script can define any of these "common" functions that it wishes to have activated. Once again, if one of these functions is not present in your script, Layout LScript will default its behavior (which is to say, an empty internal function is invoked instead). This is true even for the process() function.

Again, Layout Generic is the exception to these standards. Layout Generic scripts have a single required function, generic(), where processing takes place when the script is activated. Refer to the Layout Generic LScripts section later on this page for more information.

Layout Globals

Layout LScript establishes various "global" values that Layout scripts can use during its processing. These values are read-only (e.g, they cannot be assigned values), and can be accessed as though they were declared locally by your script.

The current globals available to Layout scripts are:

       INSTANCE    a read-only character string that represents an identifier
                   that is guaranteed to be unique for each instance of a
                   script, regardless of plug-in architecture.

Scene Public Information

Each Layout LScript script is provided access to the "public" information contained in each scene. Public information is available regarding Meshs, Bones, Lights, Camera and Scene settings. This information is accessed through the use of a Layout LScript function called getfirstitem().

Getfirstitem() returns a value that represents the first object of the type you desire. You select from the available object types by providing getfirstitem() with a parameter value (pre-defined by Layout LScript for convenience) that indicates the object list from which to pull. These pre-defined values are:

       MESH         select from among the available Layout mesh objects
       LIGHT        select from among the defined Layout light sources
       CAMERA       get a value that represents the Layout camera
       SCENE        get a value that contains information about scene settings
Getfirstitem() will also accept a character string that should represent the name of an object that currently exist in Layout. This name should be identical to that displayed to the user in any object-selection panel within Layout.

Named objects can include Lights, Mesh data, or Bones.

       filllight = getfirstitem("FillLight");
You might have noticed that an access identifier for Bones is missing. The reason for this is that Bone objects are considered children of Mesh objects; Bones cannot exist by themselves. As such, access to Bone information must take place through the parent Mesh object. Bone objects essentially become data members of the LScript Mesh object.

As the name implies, getfirstitem() will return the first object (according to LightWave Layout) that resides in the list of the objects of the specified type. However, if no items of the specified type exist in the current scene, then getfirstitem() will return 'nil'. The following code snippet illustrates how you might use getfirstitem() to begin a sequential scan of the lights currently defined in the scene:

        ...
        light = getfirstitem(LIGHT);

        if light != nil then
        begin
            // scan through all lights
            ...
        end
        else
            // we're animating by Braille...
            ...
The data type returned by getfirstitem() are "instances" of an internal LScript object type designed to provide an interface to, or "wrapper" around, the Layout object returned. Unlike normal LScript data types (string, integer, etc.), this data type is "alive." Not only does it provide access to data that is associated with the Layout object, but in most cases these object "instances" provide functions that you can invoke to perform some operation on the Layout object or its data. In the language of C++, these functions are referred to as "methods," and the object variables containing data are referred to as "data members." These "wrappers" for the Layout objects are known as Layout Object Agents in LScript parlance, but we will refer to them simply as Object Agents.

The LScript data type known as a "FileObject" is also a type of Object Agent, used as agents for disk files.

Getfirstitem() returns an Object Agent for the first Layout object, but it provides no capability for randomly accessing Layout objects, nor is there a companion function to getfirstitem() that will return the next Layout object in the list. How, then, do you access other objects of the same type?

Once you have the first Object Agent off the list, you have in your possession the ability to access all other Layout objects of that class in sequential order. Each Object Agent returned by a call to getfirstitem() provides (or "exports") some common functions (or "methods") that allow each Object Agent to perform the same functions--even though they my do so in different ways. In a purely object-oriented language like Smalltalk, these common methods are known as "messages" that an object recognizes and to which it can respond. When instances of different "classes" (definition of objects) respond to the same message, they are considered to be "polymorphic."

One such method that each Object Agent exports is called "next." The next() method will return the next Layout object (as a LScript Object Agent instance) in the list of the same object category. By way of example, let's see how we might accomplish a traversal of Layout Light objects in a Layout LScript script.

        light = getfirstitem(LIGHT);

        while(light != nil)
        {
            ...code to process this light instance...

            light = light.next();
        }
Each Object Agent returned by getfirstitem() (or, for that matter, those returned by any Object Agent method that returns another Object Agent) will respond to the following common methods and contains the following common data members:

       parent

                     points to an Object Agent that represents the Layout
                     object that is considered the parent of this Layout
                     object (or 'nil' if there is none)

       target

                     points to an Object Agent that represents the Layout
                     object that is designated as the target for this Layout
                     object (or 'nil' if there is none)

       type

                     holds a constant (i.e., read-only) value that identifies
                     the type of this Layout object (one of MESH, BONE, LIGHT,
                     CAMERA, or SCENE)

       goal

                     points to an Object Agent that represents the Layout
                     object that is designated as the goal for this Layout
                     object (or 'nil' if there is none)

       name

                     holds a character string that represents the name of this
                     Layout object (as it appears to the user in an Layout
                     object list)

       getPosition(time)        -> vector
       getRight(time)           -> vector
       getUp(time)              -> vector
       getForward(time)         -> vector
       getRotation(time)        -> vector
       getScaling(time)         -> vector
       getPivot(time)           -> vector
       getWorldPosition(time)   -> vector
       getWorldRight(time)      -> vector
       getWorldUp(time)         -> vector
       getWorldForward(time)    -> vector

                     each of these methods returns a vector containing the
                     three numeric values corresponding to the parameter
                     at the specified time index.

       limits(state)            -> array[6]

                     returns an array of six (6) numbers that represent the
                     minimum [elements 1-3] and maximum [elements 4-6] limits
                     that have been established on a particular Layout object
                     state (POSITION, RIGHT, UP, FOWARD, ROTATION, SCALING,
                     PIVOT or WPOSITION)

       filename

                     holds a character string that represents the filename
                     (including path, if any) that contains this Layout object

       pointcount

                     when appropriate, contains an integer value that
                     represents the number of points in the Layout object, or
                     'nil' when not appropriate (i.e., CAMERA)

       polycount

                     when appropriate, contains an integer value that
                     represents the number of polygons in the Layout object,
                     or 'nil' when not appropriate (i.e., CAMERA)

       shadows[]

                     holds an array of three (3) boolean values that represent
                     the Layout object's current shadow options:

                         [1] == true if Self Shadow is on, or false if off
                         [2] == true if Casts Shadow is on, or false if off
                         [3] == true if Receive Shadow is on, or false if off

       dissolve(time)           -> number

                     returns the percentage (where 1.0 equals 100%) that this
                     Layout object is dissolved at the specified 'time' index

       next()                   -> Object Agent

                     returns the next Layout object (as a LScript Object Agent) in
                     the list of the same category, or 'nil' if there is none

       firstChild()             -> Object Agent

                     returns a Object Agent that represents the Layout object
                     that is designated as the first child belonging to this
                     Layout object (or 'nil' if there is none)

       nextChild()              -> Object Agent

                     returns the next Object Agent that represents the Layout
                     object that is designated as a child of this Layout
                     object (or 'nil' if there is none)

       bone()                   -> Object Agent

                     returns the first Object Agent that represents the Layout
                     object that is designated as the first bone assigned to
                     this Layout object (using the Object Agent method next(),
                     you can traverse all Layout bones that are assigned to
                     this Layout object)
Because not all Layout objects contain the same types of data, some Object Agents will only offer a subset of these data members and respond to a subset of these exported methods. For instance, Object Agents of type SCENE will return 'nil' for most of these methods, and the CAMERA Object Agent contains no points or polygons.

A number of exported Object Agent methods listed previously accept a parameter that needs to be provided as a "time index." This index value represents the offset (expressed as time) from the beginning of the specific span of animation frames contained in the scene. To calculate the time index in which you have interest, simply divide the frame number by the frames-per-second setting for the scene. For instance, frame number 163 of a 30-frames-per-second animation would be the (163 / 30) second time index, or 5.43 seconds into the animation.

Objects In-depth

While all Object Agents share the aforementioned common data members and exported methods, certain Object Agents contain additional data members or methods. The reason for this is that Layout objects will often contain data that are specific to their functionality (Cameras have a zoomfactor that would be meaningless to Bones), and Object Agents must provide the Layout LScript script programmer the means to access--and possibly modify--this data.

At the time of this writing, LightWave 3D is currently in its fifth major release, and as of LightWave 3D version 5.0, all data members discussed in this section are strictly read-only. Modifying them will have no effect on the Layout objects themselves, and will likely generate a run-time error when the script is executed.

Bones

       flags[]

                     holds an array of two (2) boolean values that represent
                     the Bone's current options:

                         [1] == true if Bone is active, or false if not
                         [2] == true if Bone is limited, or false if unlimited

       restparam(state)         -> array[3]

                     returns an array of three (3) numbers that represent the
                     value of a particular Bone state.  The state value can be
                     one of POSITION, RIGHT, UP, FORWARD, ROTATION, SCALING,
                     PIVOT or WPOSITION ("world" position)

       restlength

                     contains a floating-point number that represents the rest
                     length of the Bone in meters

       innerlimit

                     contains a floating-point number that represents the
                     inner limit radius of the Bone (only valid if flags[2] is
                     'true')

       outerlimit

                     contains a floating-point number that represents the
                     outer limit radius of the Bone (only valid if flags[2] is
                     'true')

Lights

       ambient(time)            -> vector
       rgbambient(time)         -> array[3]

                     returns a vector or array of three (3) values that
                     represent the global ambient Light's color values
                     at the specified 'time' index.  ambient() returns
                     the channel value as a percentage between 0.0 and 1.0,
                     while rgbambient() returns the channel value as an
                     integer between 0 and 255.

       type

                     holds a constant (read-only) integer value that
                     identifies the type of this Light (one of DISTANTLIGHT,
                     POINTLIGHT, or SPOTLIGHT)

       color(time)              -> vector
       rgbcolor(time)           -> array[3]

                     returns a vector or array of three (3) values that
                     represent the Light's color values at the specified
                     'time' index.  color() returns the channel value as
                     a percentage between 0.0 and 1.0, while rgbcolor()
                     returns the channel value as an integer between 0
                     and 255.

       shadowtype

                     holds a constant (read-only) integer value that
                     identifies the shadow type of this Light (one of
                     SHADOWRAYTRACE, SHADOWMAP or SHADOWOFF)

       coneangles[]

                     holds an array of two (2) floating-point numbers that
                     represent the cone angles for this Light if it is of type
                     SPOTLIGHT.  The first element [1] represents the radius
                     as half the total Spotlight cone angle, and element [2]
                     indicates the angular width of the Spotlight's soft edge.

Camera

       zoomFactor(time)         -> number

                     returns a floating-point number that represents the
                     Camera's zoom factor at the specified 'time' index

       focalLength(time)        -> number

                     returns a floating-point number that represents the
                     Camera's focal length at the specified 'time' index

       focalDistance(time)      -> number

                     returns a floating-point number that represents (in
                     meters) the Camera's focal distance at the specified
                     'time' index

       fStop(time)              -> number

                     returns a floating-point number that represents the
                     Camera's f-stop setting at the specified 'time' index

       blurLength(time)         -> number

                     returns a floating-point number that represents (in
                     meters) the Camera's blur length at the specified 'time'
                     index

       fovAngles(time)          -> array[2]

                     returns an array of two (2) floating-point numbers that
                     represent the Camera's field-of-view angles at the
                     specified 'time' index.  the first element [1] represents
                     the horizontal angle, the second element [2] represents
                     the vertical angle.  these angles (measured in radians)
                     are centered around the Camera's direction

Scene

       name

                     holds a character string that represents the name of this
                     Scene

       filename

                     holds a character string that represents the filename of
                     this Scene

       totalpoints

                     contains an integer value that represents the total
                     number of Mesh points in the Scene

       totalpolygons

                     contains an integer value that represents the total
                     number of Mesh polygons in the Scene

       rendertype

                     holds a constant (read-only) integer value that
                     identifies the type of rendering that will take place in
                     the Scene (one of WIRERENDER, QUICKRENDER, or
                     REALISTICRENDER)

       renderopts[]

                     holds an array of eight (8) boolean values that represent
                     the Scene's current options:

                         [1] == true if Shadow Tracing is active
                         [2] == true if Reflection Tracing is active
                         [3] == true if Refraction Tracing is active
                         [4] == true if Field Rendering is active
                         [5] == true if Reverse Field Rendering is active
                         [6] == true if Motion Blur is active
                         [7] == true if Depth-Of-Field is active
                         [8] == true if Limited Region is active

       framestart

                     contains an integer value that represents the beginning
                     frame number to render

       frameend

                     contains an integer value that represents the ending
                     frame number to render

       framestep

                     contains an integer value that represents the frame step
                     value

       fps

                     contains an integer value that represents the
                     frames-per-second setting for the Scene (default is 30)

       framewidth

                     contains an integer value that represents the width of
                     the frames to be rendered

       frameheight

                     contains an integer value that represents the height of
                     the frames to be rendered

       pixelaspect

                     contains a floating-point number that represents the
                     pixel aspect ratio of the Scene (as pixel-width /
                     pixel-height).

       minsamplesperpixel

                     contains an integer value that represents the minimum
                     number of samples per pixel in the final image based on
                     the current rendering options.

       maxsamplesperpixel

                     contains an integer value that represents the maximum
                     number of samples per pixel in the final image based on
                     the current rendering options.

       limitedregion[]

                     holds an array of four (4) floating-point numbers that
                     represent the location of the Scene's limited region
                     area:

                         [1] == x0
                         [2] == y0
                         [3] == x1
                         [4] == y1

Extending The Requester Interface: Panel Services

In the options() function, a Layout script can use requester code that is virtually identical to that used by Modeler LScript. In fact, requester code can actually be transferred between scripts without modification. However, unlike Modeler, LightWave Layout does not contain integral interface code that is made available to user-written plug-ins. Instead, Layout employs the Panel Services plug-in to provide interface features to user-written plug-ins. LScript takes advantage of the Panel Services plug-in to facilitate its own requester functionality.

Because Panel Services makes available controls that add more functionality than those found in LightWave 3D Modeler, Layout LScript passes this ability on to the Layout script writer in the form of new functions. Even though these new controls could be considered "non-standard", they have been designed within Layout LScript to function in a fashion similar to other controls.

Because Panel Services is a plug-in, the possibility exists that the plug-in may not be available at the time your LScript attempts to post a requester panel. To allow the script writer the ability to detect the absence of this critical plug-in, the reqbegin() command has been extended in Layout LScript to return a logical 'true' or 'false' value to indicate the presence of the Panel Services plug-in. A logical 'true' indicates its presence. You can use this indicator to decide whether or not you have the required facilities to post requesters from your script.

        ...
        if(!reqbegin("My Requester"))
        {
            error("Requester support unavailable");
            return;
        }
        ...
In support of Panel Services, five new control creation commands have been added to the Layout LScript command set. These functions are only available in Layout scripts; Modeler LScript does not support Panel Services-equivalents of these control commands.

         ctlfilename(title,filename) -> string

                creates a control that contains an text edit field and
                a push button that will post a file-requester dialog
                box.  the selection returned from the file requester will
                be placed into the text edit field.

                'title' is a string value representing the label of the
                control

                'filename' is a string value representing the initial
                value of the filename edit field of the control

         ctlrgb(title,rgb) -> vector

                creates a control that contains three numeric entry fields
                for typing in color components and a preview area where the
                entered values are combined and displayed.

                'title' is a string value representing the label of the
                control

                'rgb' is a vector value representing the Red, Green and Blue
                color components in the 'x', 'y', and 'z' positions. each of
                these values is an integer number between 0 and 255.  This
                parameter can also be a single integer value that will be used
                with all channels, or it can be three individual integer
                values corresponding to each of the color channels.

         ctlhsv(title,hsv) -> vector

                creates a control that contains three numeric entry fields
                for typing in color components and a preview area where the
                entered values are combined and displayed.

                'title' is a string value representing the label of the
                control

                'hsv' is a vector value representing the Red, Green and Blue
                color components in the 'x', 'y', and 'z' positions. each of
                these values is an integer number between 0 and 255.  This
                parameter can also be a single integer value that will be used
                with all channels, or it can be three individual integer
                values corresponding to each of the color channels.

         ctlcheckbox(title,state) -> boolean

                'title' is a string value representing the label of the
                control

                'state' is a boolean value that indicates the appearance
                of the checkbox.  a logical 'true' will initially check
                the control, while a logical 'false' will leave it
                unchecked

         ctlallitems(title)     -> Object Agent
         ctlmeshitems(title)    -> Object Agent
         ctlcameraitems(title)  -> Object Agent
         ctllightitems(title)   -> Object Agent
         ctlboneitems(title)    -> Object Agent
         ctlimageitems(title)   -> Object Agent

                these functions create controls that provide a drop-down
                list of the specified types of Layout objects currently
                available in the scene.  the return value from each of these
                controls is an Object Agent that represents the LightWave
                object that was selected, or 'nil' if no object was selected.

                'title' is a string value representing the label of the
                control

Architectural Specifics

Now that we've covered the methods, functions and data that are common to all Layout scripts, let's examine specific plug-in architectures for the extensions that they offer script programmers.

Because we will be dealing with and discussing internal data structures that LightWave 3D makes publicly available to plug-ins, it is assumed that you have some level of familiarity with the data structures and mechanisms that are employed by 3D rendering systems and some of the more common computer graphics file formats being used in the industry today (i.e., Targa).

LS/IF (Image Filter)

The LS/IF plug-in is used to post-process frames (images) that LightWave 3D has rendered in their entirety. This LScript plug-in is granted access to internal data buffers maintained by Layout that contain, among other things, the Red, Green and Blue (RGB) color values and Alpha-Channel values that were generated as a result of a render. LS/IF is used to apply effects to these color values that represent the viewable frame.

Selecting Image Buffers

LS/IF supports an optional script function called flags(). Your script uses this function to communicate with Layout concerning which of the available buffers your script wishes to access. LightWave Layout uses this mechanism to help conserve memory; if your script does not require a particular buffer, Layout will not have to allocate memory and CPU time to make copies of these buffers available to you.

There are a number of different data buffers that Layout uses or creates as a result of its rendering activity, and your LS/IF script can request access to any or all of these buffers. Regardless of which buffers you request, however, all LS/IF scripts have implicit access to the Red, Green, Blue and Alpha-Channel buffers maintained by Layout. You do not have to explicitly request these buffers.

With the exception of the implicit buffers (Red, Green, etc.), all Layout-maintained buffers are strictly read-only. Their contents are intended to be used to influence your modifications of those buffers whose data are meant to be altered.

The buffers to which a LS/IF script can request access are:

        RED, GREEN, BLUE, ALPHA

              these buffers are available to every script, and represent the
              individual RGB and Alpha values of the rendered frame. off all
              buffers available, only these may be modified. all others are
              strictly read-only

        SPECIAL

              this value is assigned by the user on a surface by surface basis
              which is used only for this filter.  this is designed to be used
              to activate the post-processing effect for specific surfaces,
              and user-assigned percentages show up here as 0-255 values in
              the buffer

        LUMINOUS, DIFFUSE, MIRROR, TRANS, RAWRED, RAWGREEN, RAWBLUE

              these eight buffers are the raw values of the surface parameters
              before shading

        SHADING

              this buffer is a picture of the diffuse shading applied to the
              raw shapes in the image

        SHADOW

              this buffer indicates where shadows are falling in the final
              image.  it may also be thought of as an illuminations map,
              showing what parts of the image are visible to the lights in the
              scene

        GEOMETRY

              the values in this buffer are computed from the dot-product of
              the surface normal with the eye vector. it reveals something
              about the underlying shape of the objects in the image.  where
              this buffer is 255 (or 1.0) the surface is facing directly
              toward the camera, and where this buffer is 0, the surface is
              edge-on to the camera

        DEPTH

              this buffer is a map of the distance of each pixel from the
              camera plane.  this buffer differs from all the others in that
              it is floating point, and because it is not anti-aliased or
              motion-blurred
You can communicate to Layout the buffers it should make available to your script by returning one or more of these pre-defined values from your flags() function. In fact, the sole purpose of the flags() function is to do nothing but return one or more of these values. For example, the following code snippet illustrates a sample flags() function used to merely consume CPU time by requesting the implicit buffers:

        flags
        {
            return(RED,        // select the defaults just for fun
                   GREEN,
                   BLUE,
                   ALPHA);
        }

Processing Image Buffers

As with most Layout scripts, the central point of processing in a LS/IF script takes place in the process() function. The process() function is invoked only once for every frame of animation that LightWave 3D Layout generates, and any manipulations the script is designed to apply to the image pixels should be rendered to the entire image at that time.

LS/IF passes five parameters to the process() function when it is invoked. These parameters are designed to assist the process() function in accessing data in the Layout buffers by providing such information as the image width and height, as well as the current frame number being rendered. The five parameters passed to a LS/IF process() function are listed below.

        width

              an integer value that indicates the width (number of horizontal
              lines) of the generated image.  this value will be equal to the
              value that the user has set under the Layout Camera information
              panel.

        height

              an integer value that indicates the height (number of vertical
              lines) of the generated image.  this value will be equal to the
              value that the user has set under the Layout Camera information
              panel.

        frame

              an integer value that indicates the frame number of the
              generated image

        start

              a floating-point number that represents the start time of this
              frame as a time index value.

        end

              a floating-point number that represents the end time of this
              frame expressed as a time index value.

              this index value will be the same as the start time unless the
              frame has had motion-blur apply by Layout. In this case, the
              difference between the starting and ending time index is the
              "exposure time" for the frame
LS/IF scripts can call upon specialized, internal functions for accessing and updating the Red, Green, Blue and Alpha-Channel image buffer data. These functions are only available to a LS/IF function, and are not shared among other Layout scripts.

        bufferline(bufferid,line)       -> array[]

              used to retrieve the values in the buffer identified by
              'bufferid' for the indicated vertical image 'line'. 'bufferid'
              is one of the buffer indicators discussed previously (SPECIAL,
              DIFFUSE, RED, etc.).  if a buffer is requested that was not
              selected in the flags() function, a run-time error will occur.
              returns an array of buffer values whose count of elements
              matches the 'width' parameter passed to the process() function

        floatline(bufferid,line)        -> array[]

              this function is identical to bufferline(), except that it is
              used to access buffers that contain floating-point data
              (currently only the DEPTH buffer qualifies)

        setrgb(col,row,buf)

              used to set the Red, Green and Blue value for a particular image
              pixel at the specified row/column of the image.  'buf' is an
              array of three color values that represent Red ([1]), Green
              ([2]) and Blue ([3])

        setalpha(col,row,bufval)

              used to set the Alpha value for a particular image pixel at the
              specified row/column of the image

        processrgb(row,red,green,blue,alpha)

              this function is a replacement for both the setrgb() and
              setalpha() functions, and will process all four buffers at once
              for the specified image row.  because individual buffer elements
              are processed all at once--instead of one at a time, as is the
              case with setrgb() and setalpha()--this function tends to be
              much faster
Please note that, as in all cases concerning LScript arrays, the values that you use as image row/column indicators should always be "1-based", meaning that the first element in each buffer is always [1]. If you attempt to access the first element as [0], a run-time error will be generated, and the LS/IF script will disable itself from further activation.

Caching Image Data

As the name implies, Image Filter plug-ins are quite literally filters. Data comes in one end from Layout, and passes out the other end directly back to Layout. When you call bufferline() to retrieve a copy of the data in Layout's render buffer, you must process that line, and pass it back to Layout using any one of the available processing commands. In a typical processing situtation, however, once you pass this modified data back to Layout, you cannot access it again. Further calls to bufferline() requesting the same scan-line will simply return to you the original, unmodified scan-line data. The image data you modified is now safely tucked away into a separate buffer, forever beyond your reach.

LS/IF provides an internal mechanism to script writers that will take the rendered image data provided by Layout and cache it locally. This allows for such procedures as processing image data in multiple passes, or processing only portions of the image without having to cycle through all of the data. This mechanism does not necessarily provide speed improvements to LS/IF scripts; rather it provides convenience to the script writer.

To enable this caching feature, place a call to the following function at the head of your LS/IF script's process() function:

        cache()

              this function causes the LS/IF script instance to acquire all
              rendered image data into the local memory space of the script to
              facilitate multiple accesses to the same data.
Once you have enabled caching, all other LS/IF commands and functions are used just as though you were performing true filtering. However, these function calls will be redirected to retrieve and update the local data within the script instance instead of the data being buffered by Layout.

When caching is enabled, two new functions become available that allow direct row/column access to pixel data. These functions are only active when and after the cache() call is made in your Image Filter script.

        getpixel(row,col)       -> array[4]

              this function accepts row and column integer values, specifying
              an exact location in the pixel data buffer.  it returns
              the Red, Green, Blue, and Alpha values at that location.

        putpixel(row,col,red,green,blue,alpha)

              pixel data can be directly modified using this value.  the pixel
              values at the specified row and column offset in the pixel
              buffer will be altered to reflect the values provided for
              the Red, Green, Blue, and Alpha channels.
There is no need to explicitly "flush" the new internal image data once caching is enabled. The LS/IF plug-in will automatically perform a complete update to Layout of the modified data when your script's process() function terminates.

LS/IF Logic Flow

Here we will examine the flow of control that passes through a LS/IF script as it is invoked by LightWave Layout.

The sequence of processing within a LS/IF script will typically begin with the flags() function. Remember that this function is optional, and if you do provide a flags() function in your script, then only the Red, Green, Blue and Alpha-Channel buffers will be available for access later in your process() function. For the sake of illustration, we call once again upon our space- consuming flags() function that selects the implicit buffers:

        flags
        {
            return(RED,GREEN,BLUE,ALPHA);
        }
If your script is active, Layout attempts to add your processing to the rendered image by invoking your process() function. This will typically occur immediately after the image has been rendered, but before it is displayed to the user or saved to disk. Here is an incomplete process() function that shows the typical flow of processing that a script will want to duplicate for each image:

        process: width, height, frame, starttime, endtime
        {
            out[3] = nil;    // holds new values passed back to Layout

            for(i = 1;i <= height;++i)
            {
                red   = bufferline(RED,i);
                green = bufferline(GREEN,i);
                blue  = bufferline(BLUE,i);
                alpha = bufferline(ALPHA,i);

                for(j = 1;j <= width;++j)
                {
                    // perform some manipulation on the buffer values
                    ...

                    setrgb(j,i,out);
                    setalpha(j,i,alpha[j]);
                }
            }
        }
It should be noted that, whether or not you perform any modifications to a particular image pixel or Alpha-Channel value, unless you have enabled caching with a call to cache(), a call to setrgb() and setalpha() (or, alternately, processrgb()) must be made. As the name implies, LS/IF is an image filter, and like all filters, things are expected to enter, while something useful passes through to eventually emerge in the output. LightWave Layout expects every pixel of the Red, Green, Blue and Alpha-Channel buffers to pass through this filter in order to generate a complete image. If you fail to process a pixel into Layout's output--whether or not you altered its value--then the resulting location in the final image will have an undefined value RGB (typically, each channel is assigned zeros (0), which equates to black in the output).

Using The Monitor

Unique among Layout LScript plug-ins, LS/IF provides access to a progress monitor, quite similar to that offered by Modeler LScript. Because a great deal of processing can take place inside an Image Filter plug-in (imagine having to perform some function on each pixel of a 640x480 image for each frame of animation!), a visual indicator is used to provide feedback to the viewer concerning the progress of processing.

The following functions are used to manage Layout-provided monitor access. As you will see, usage of these functions is almost identical to those provided within Modeler LScript.

        moninit(steps)

              initializes the monitor system, indicating the anticipated
              number of steps required to complete the processing task

        monstep([advance])

              advances the monitor indicator by the optional integer value
              provided.  if no argument is provided, then the advance value
              is 1

        monend()

              terminates the monitor system

////    ////    ////    ////    ////    ////    ////    ////    ////    ////
///                                                                     ///
//                S T A R T     C O N S T R U C T I O N                 //
/                                                                       /
    ////    ////    ////    ////    ////    ////    ////    ////    ////

LS/PT (Procedural Texture)

Procedural textures (or Shaders, as they are commonly referred to) are surface appearances that are calculated at render-time based upon specific values. LS/PT is a shader scripting plug-in, and can be used to give a surface a desired texture or color.

The included example script, Blotch, creates a colored spot on a surface.

Shader Functions

LS/PT contains two of its own specific support functions.

        init()

                     invoked at the start of a sequence of frames.  use to
                     initialize script values that are needed to properly
                     generate a texture

        cleanup()

                     invoked when a render sequence is complete

        newtime(frame,time)

                     invoked at the start of each new time within the current
                     render sequence.  an integer value 'frame' indicates the
                     current frame number, while the number 'time' specifies
                     the current time index

        flags()

                     a LS/PT script needs to indicate to Layout which
                     attributes of a surface's texture it will modify.  one or
                     more of the following values may be returned from the
                     flags() function:

                           NORMAL
                           COLOR
                           LUMINOUS
                           DIFFUSE
                           SPECULAR
                           MIRROR
                           TRANSPARENT
                           ETA
                           ROUGHNESS
                           RAYTRACE

                     if a LS/PT script intends to utilize the raytrace()
                     method (discussed presently), then it should include
                     in the flags() return value the RAYTRACE flag.

Processing Textures

As with all Layout scripts, the processing point of your LS/PT script is the process() function. The process() function is called on a per-pixel basis.

LS/PT provides a single argument to your script's process() function. This argument is an instance of a Object Agent, known as a ShaderObject. This object contains data members and methods that can be accessed (and in some cases, modified).

A LS/PT ShaderObject has the following members/methods:

        sx                (READ-ONLY)

                 number representing the spot X location in the final
                 image in pixel coordinates where (0,0) is at the
                 upper-left corner

        sy                (READ-ONLY)

                 number representing the spot Y location in the final
                 image in pixel coordinates where (0,0) is at the
                 upper-left corner

        oPos[3]           (READ-ONLY)

                 numbers that represent the  coordinates of the
                 spot position in object coordinates

        wPos[3]           (READ-ONLY)

                 numbers that represent the  coordinates of the
                 spot position in world coordinates

        gNorm[3]          (READ-ONLY)

                 numbers representing the geometric normal of the spot in
                 world coordinates.  this is the raw polygonal normal at
                 the spot, unperturbed by smoothing or bump mapping

        spotSize          (READ-ONLY)

                 number representing the approximate spot diameter.  this
                 is a very approximate value since spots on a surface
                 viewed on edge are long and thin.  this can be used to
                 compute texture anti-aliasing

        raySource[3]      (READ-ONLY)

                 numbers representing the origin of the incoming viewing
                 ray in world coordinates.  often this will be the camera
                 but it does not have to be

        rayLength         (READ-ONLY)

                 a number representing the distance the viewing ray
                 travelled in free space to reach this spot

        cosine            (READ-ONLY)

                 a number representing the cosine of the angle between the
                 viewing ray and the surface normal at this spot.  it
                 indicates how glancing the view is and gives a measure of
                 how approximate the spot size is

        oXfrm[9]          (READ-ONLY)

                 object-to-world transformation matrix.  this can be
                 computed other ways, but are included here for speed and
                 are intended to be used primarily for directional
                 vectors.

        wXfrm[9]          (READ-ONLY)

                 world-to-object transformation matrix.  This can be
                 computed other ways, but are included here for speed and
                 are intended to be used primarily for directional
                 vectors.

        objID             (READ-ONLY)

                 a pointer to an Object Agent that represents the object being
                 shaded

        polNum            (READ-ONLY)

                 an integer that represents the polygon number of the
                 object being shaded.  While this will be the polygon
                 number for normal mesh objects, it may represent other
                 sub-object information in non-mesh objects.

        wNorm[3]

                 new geometric normal of the spot in world coordinates.
                 Modifying this makes the surface look bumpy without altering
                 the geometry (bump mapping).  The shader needs to re-normalize
                 the vector after perturbation.

        color[3]

                 numbers representing the percentage of the Red [1], Green
                 [2], and Blue [3] values of the surface color, where 1.0
                 equals 100%

        luminous

                 a number representing the percentage of luminousity of the
                 surface (1.0 == 100%)

        diffuse

                 a number representing the percentage of diffusion of the
                 surface (1.0 == 100%)

        specular

                 a number representing the percentage of specularity of the
                 surface (1.0 == 100%)

        mirror

                 a number representing the percentage of mirroring of the
                 surface (1.0 == 100%)

        transparency

                 a number representing the percentage of transparency of the
                 surface (1.0 == 100%)

        eta

                 a number representing the percentage of the index of
                 refraction of the surface (1.0 == 100%)

        roughness

                 a number representing the percentage of roughness of the
                 surface (1.0 == 100%)
To set the perceived color directly, a shader script can set all the parameters to zero except for luminous which is 1.0 and color which is the output color of the spot.

ShaderObject Methods

ShaderObject's provide two methods can be used in generating a surface texture.

        illuminate(light,position)      -> array[6]

                 this function returns an array of six (6) numbers that
                 represent the light ray (color[1-3] and direction[4-6])
                 hitting the given position from the given light at the
                 current instant.  the return value is zero if the light does
                 not illuminate the given world coordinate position at all.
                 the color includes effects from shadows (if any), falloff,
                 spotlight cones and transparent objects between the light and
                 the point

        raytrace(position,direction)    -> array[4]

                 this function may be called to trace a ray from the a given
                 location in a given direction (in world coordinates).  the
                 function returns an array of four (4) elements that represent
                 the length of the ray [1] and the color coming from that
                 direction [2-4].  the ray length will be -1.0 if it is
                 infinite.  the direction used is the outgoing direction and
                 must be normalized to be a unit vector

LS/DM (Displacement Map)

The LS/DM plug-in is used to calculate the location of an object's point based on a given frame and/or time. Unlike LS/IF, whose processing can be accomplished in a single invocation, a LS/DM script is called on a per-point basis.

Displacement Functions

LS/DM offers the following displacement-specific functions:

        newtime(object,frame,time)

                 as with LS/PT, this function is invoked at the start of
                 each new time within the current sequence.  the parameters
                 provided to newtime() are:  an Object Agent that represents the
                 object containing the point to be processed; an integer
                 representing the frame number; and a number representing the
                 current time index.

        flags()

                 this function returns (only) one of two possible values, that
                 indicate whether displacements will take place in world
                 coordinates (WORLD) or in the object's local coordinates
                 (LOCAL)

Processing Displacement

The LS/DM process() function is provided with a single argument. This argument is an instance of a DisplacementObject.

A LS/DM DisplacementObject contains the following data members:

        oPos[3]     (READ-ONLY)

                 numbers representing the  position of the point
                 being processed at its origin.  these values do not
                 change, and should be used to calculate offsets based
                 upon the passage of time

        source[3]

                 numbers representing the new  position of the point
                 being processed for the current frame/time
No methods are provided by the DisplacementObject.

LS/IA (Item Animation)

LS/IA, an Item Animation plug-in, allows you to influence an object's translations during an animation sequence. You can modify an object's position, rotation, and scaling, overriding that calculated by Layout.

Processing Motion

LS/IA provides three arguments to the process() function. The second and third parameters are the integer frame number and numeric time index of the current invocation. The first argument is an instance of a MotionObject.

The LS/IA MotionObject provides the following object methods:

        get(attribute,time)         -> vector

                 this function will return a vector that represents the value
                 of the specified 'attribute' at the given 'time'. 'attribute'
                 can be one of POSITION, ROTATION, or SCALING. 'time' is the
                 time index in which you are interested.  in the case of
                 ROTATION, the  values are in degrees.  SCALING values
                 are multipliers, where 1.0 indicates no change.

        set(attribute,value)

                 this function allows you to set the specified 'attribute'
                 (POSITION, ROTATION, SCALING) to the provided vector 'value'
A LS/IA MotionObject contains the following data members:

        objID             (READ-ONLY)

                 a pointer to the Object Agent to which this particular plug-in
                 has been assigned

LS/OR (Object Replacement)

LS/OR allows you to arbitrarily replace an object in Layout on a frame-by-frame basis.

Processing Replacements

The process() function of a LS/OR plug-in receives a single argument. This argument is an instance of a ReplacementObject. The LS/OR ReplacementObject provides the following data members:

        objID        (READ-ONLY)

                 an Object Agent that represents the object whose geometry you
                 are replacing

        curFrame     (READ-ONLY)

                 an integer that represents the frame number for the currently
                 loaded geometry.

        curTime      (READ-ONLY)

                 a number that represents the time index for the currently
                 loaded geometry.

        newFrame     (READ-ONLY)

                 an integer value that represents the frame number for the
                 next step.  new geometry should be loaded if the object needs
                 to look different at this new frame

        newTime      (READ-ONLY)

                 a number that represents the time index for the next step.
                 new geometry should be loaded if the object needs to look
                 different at this new time index.  'curTime' and 'newTime'
                 may not be sequential, since network rendering can cause the
                 renderer to jump around between non-sequential times

        curType      (READ-ONLY)

                 a constant value that indicates the current type of rendering
                 to be done.  the script can provide different geometry for
                 interactive previewing and actual rendering by examining this
                 value.  this value can be one of NONE, PREVIEW, or RENDER.
                 NONE is present if no geometry is loaded for the current time
                 index.  PREVIEW indicates that a Layout preview is being
                 generated, where RENDER is used when a complete render is
                 being done

        newType      (READ-ONLY)

                 a constant that indicates the type of rendering that will
                 be done at the next frame/time index.  this member can be
                 one of NONE, PREVIEW, or RENDER

        curFilename  (READ-ONLY)

                 a string value that represents the object geometry file
                 currently loaded, and may be 'nil' if there is no geometry
                 loaded.

        newFilename

                 a string value that represents the filename of a new object
                 file to be loaded as the geometry for this item at the new
                 time index, and is the only data member set by the script. it
                 should only be set if the new geometry differs from that
                 currently loaded, since loading new geometry incurs
                 significant overhead
////    ////    ////    ////    ////    ////    ////    ////    ////    ////
///                                                                     ///
//                   E N D      C O N S T R U C T I O N                 //
/                                                                       /
    ////    ////    ////    ////    ////    ////    ////    ////    ////

LS/GN (Layout Generic)

Finally, we come to Layout Generic scripting. LS/GN allows you to access Layout internal objects and data without the need to be associated with any scene or scene component. Layout Generic scripts can access all scene components and settings, as well as all functions and data belonging to the LScript engine (preprocessor, Requester support, IPC queues, etc.).

Generic Script Construction

Layout Generic scripts are very similar to the format used by Modeler scripts. There is a single, required function in all Layout Generic scripts, and this function is called generic(). When a Layout Generic script is invoked, execution will begin with this function, and when this function terminates, execution of the script will be complete.

Here is a simple Layout Generic script to report on the number of vertices in an object currently loaded into Layout:

            generic
            {
                if(!reqbegin("Point Count"))
                    return;     // no Panel Services plug-in

                c1 = ctlallitems("Reference object");

                if(reqpost())
                {
                    if((item = getvalue(c1)) == nil)
                        return;
                }
                else
                    return;

                reqend();

                info("Object ",item.name," has ",item.pointcount," points.");
            }

Generic Functions

The Layout Generic architecture makes available two functions for managing Layout scenes:

        loadscene(filename[,title])

                 this function loads the indicated 'filename' as a Layout
                 scene file.  the optional 'title' parameter will be used
                 to name the scene file internally once it is loaded.  this
                 optional name will then become the scene's new filename.
                 if this parameter is not provided, the 'filename' parameter
                 will be used in it's place.

        savescene(filename)

                 this function saves the scene that is currently loaded into
                 Layout as the provided path and 'filename'.


Next Section Previous Section Table of Contents Index Errata
© 1996 Virtual Visions, Inc.
© 1997 NewTek, Inc.